package net.dotool;

import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;

import net.dotool.CheckPolygon.GridPoint;
import net.dotool.geo.GeoUtils;
import net.dotool.geo.PointArea;
import net.dotool.geo.VectorArea;

/**
 * @author 李岩飞
 * @email eliyanfei@126.com	
 * 2018年1月15日 上午11:01:26
 * 
 */
public class GisTools {

	/*
	 * 大地坐标系资料WGS-84 长半径a=6378137 短半径b=6356752.3142 扁率f=1/298.2572236
	 */
	/** 长半径a=6378137 */
	private static double a = 6378137;
	/** 短半径b=6356752.3142 */
	private static double b = 6356752.3142;
	/** 扁率f=1/298.2572236 */
	private static double f = 1 / 298.2572236;

	
	/**
	 * 对经纬度进行排序
	 */
	public static Point sort(String lnglats, boolean cache) {
		final Point point = new Point();
		if (lnglats == null)
			return point;
		String[] gpses = lnglats.split(";");
		for (String onegps : gpses) {
			String[] arr = onegps.split(",");
			if (arr.length == 2) {
				double longitue = NumberTools.toDouble(arr[0]);
				double latitude = NumberTools.toDouble(arr[1]);
				point.set(longitue, latitude);
				if (cache) {
					point.longitudes.add(longitue);
					point.latitudes.add(latitude);
				}
			}
		}
		return point;
	}

	public static class Point {
		public double maxlng = 0;//最大经度
		public double minlng = Double.MAX_VALUE;//最小经度
		public double maxlat = 0;//最大纬度
		public double minlat = Double.MAX_VALUE;//最小纬度
		public List<Double> longitudes = new ArrayList<Double>();
		public List<Double> latitudes = new ArrayList<Double>();

		public void set(double longitue, double latitude) {
			maxlng = Math.max(maxlng, longitue);
			minlng = Math.min(minlng, longitue);
			maxlat = Math.max(maxlat, latitude);
			minlat = Math.min(minlat, latitude);
		}
	}
	

	/**
	 * 传入字符串坐标组；转为集合；114.288221,30.593268#114.321866,30.580559#114.311223,30.560015#
	 * 
	 * @param str
	 * @return
	 */
	public static List<GridPoint> strToGridPointList(String str) {
		List<GridPoint> list = new ArrayList<GridPoint>();
		String[] str1s = str.split(";");
		for (String s : str1s) {
			if (StringTools.isNotBlank(s)) {
				String[] str2s = s.split(" ");
				if(str2s.length!=2) {
					str2s = s.split(",");
				}
				if (str2s.length > 1)
					list.add(new GridPoint(NumberTools.toDouble(str2s[0], 0), NumberTools.toDouble(str2s[1], 0)));
			}
		}
		return list;
	}

	/**
	 * 获取多边形的中心
	 * 
	 */
	public static GridPoint getCenterOfPolygon(List<GridPoint> pointList) {
		if(pointList.size() == 1) {
			return pointList.get(0);
		}
		double area = 0.0;// 多边形面积
		double Gx = 0.0, Gy = 0.0;// 重心的x、y
		double lastLng = 0, lastLat = 0;
		for (int i = 1; i <= pointList.size(); i++) {
			double iLat = pointList.get(i % pointList.size()).lng;
			double iLng = pointList.get(i % pointList.size()).lat;

			lastLng = iLat;
			lastLat = iLng;

			double nextLat = pointList.get(i - 1).lng;
			double nextLng = pointList.get(i - 1).lat;
			double temp = (iLat * nextLng - iLng * nextLat) / 2.0;
			area += temp;
			Gx += temp * (iLat + nextLat) / 3.0;
			Gy += temp * (iLng + nextLng) / 3.0;
		}
		if (area == 0) {
			Gx = lastLng;
			Gy = lastLat;
		} else {
			Gx = Gx / area;
			Gy = Gy / area;
		}
		return new GridPoint(Gx, Gy);
	}
	
	/**
	 * 提供一些经纬度点，计算线的长度(米)
	 */
	public static double getPolylineDistance(List<GridPoint> points) {
		double dis = 0.0D;
		for (int i = 1; i < points.size() - 1; i++) {
			GridPoint gp1 = points.get(i);
			GridPoint gp2 = points.get(i + 1);
			dis += distance(gp1.lat, gp1.lng, gp2.lat, gp2.lng);
		}
		return dis;
	}
	
	/**
	 * 提供一些经纬度点，计算面积
	 */
	public static double getPolygonArea(List<PointArea> points) {
		double area = 0.0D;
		for (int i = 1; i < points.size() - 1; i++) {
			area += VectorArea.area(points.get(0), points.get(i), points.get(i + 1));
		}
		return area / 2;
	}
	
	/**
	 * 根据两个点的经纬度计算这两个点之间的距离
	 */
	public static double distance(double lat1, double lng1, double lat2, double lng2) {
		return GeoUtils.distanceAsMeters(lat1, lng1, lat2, lng2);
	}
	
	/**
	 * 根据小区信息，获取基站扇形信息
	 * @param longitude 经度
	 * @param latitude 维度
	 * @param degree 角度
	 * @param direction 方向角
	 * @param distance 边长
	 */
	public static List<GridPoint> getCellMapLatLng(double longitude, double latitude, int degree, int direction,
			int distance) {
		List<GridPoint> list = new ArrayList<GridPoint>();

		int raids = degree / 2;
		list.add(new GridPoint(longitude, latitude));
		int leftFxj = GeoUtils.calculateFxj(direction, 0, raids);
		double leftJd = longitude + NumberTools.CaculateCos(leftFxj, distance);
		double leftWd = latitude + NumberTools.CaculateSin(leftFxj, distance);
		list.add(new GridPoint(leftJd, leftWd));
		int leftFxj1 = GeoUtils.calculateFxj(direction, 1, raids);
		double leftJd1 = longitude + NumberTools.CaculateCos(leftFxj1, distance);
		double leftWd1 = latitude + NumberTools.CaculateSin(leftFxj1, distance);
		list.add(new GridPoint(leftJd1, leftWd1));
		return list;
	}


	/**
	 * 点到直线的最短距离的判断 点（x0,y0） 到由两点组成的线段（x1,y1） ,( x2,y2 )
	 * @param lat1 直线1点纬度
	 * @param lng1 直线1点经度
	 * @param lat2 直线2点纬度
	 * @param lng2 直线2点经度
	 * @param lat0 点维度
	 * @param lng0 点经度
	 */
	public static double pointToLine(double lat1, double lng1, double lat2, double lng2, double lat0, double lng0) {
		double degree1 = getDegree(lng1, lat1, lng2, lat2, lng0, lat0);
		double degree2 = getDegree(lng2, lat2, lng1, lat1, lng0, lat0);
		if (degree1 >= 90 || degree2 >= 90 || degree1 == 0 || degree2 == 0) {
			return Double.MAX_VALUE;
		}

		double space = 0;
		double a, b, c;
		a = distance(lat1, lng1, lat2, lng2);// 线段的长度
		b = distance(lat1, lng1, lat0, lng0);// (x1,y1)到点的距离
		c = distance(lat2, lng2, lat0, lng0);// (x2,y2)到点的距离

		if (c <= 0.000001 || b <= 0.000001) {
			space = 0;
			return space;
		}

		if (a <= 0.000001) {
			space = b;
			return space;
		}

		if (c * c >= a * a + b * b) {
			space = b;
			return space;
		}

		if (b * b >= a * a + c * c) {
			space = c;
			return space;
		}

		double p = (a + b + c) / 2;// 半周长
		double s = Math.sqrt(p * (p - a) * (p - b) * (p - c));// 海伦公式求面积
		space = 2 * s / a;// 返回点到线的距离（利用三角形面积公式求高）
		return space;
	}
	
	/**
	 * 计算角度
	 */
	private static int getDegree(double vertexPointX, double vertexPointY, double point0X, double point0Y,
			double point1X, double point1Y) {
		// 向量的点乘
		double vector = (point0X - vertexPointX) * (point1X - vertexPointX)
				+ (point0Y - vertexPointY) * (point1Y - vertexPointY);
		// 向量的模乘
		double sqrt = Math.sqrt((Math.abs((point0X - vertexPointX) * (point0X - vertexPointX))
				+ Math.abs((point0Y - vertexPointY) * (point0Y - vertexPointY)))
				* (Math.abs((point1X - vertexPointX) * (point1X - vertexPointX))
						+ Math.abs((point1Y - vertexPointY) * (point1Y - vertexPointY))));
		// 反余弦计算弧度
		double radian = Math.acos(vector / sqrt);
		// 弧度转角度制
		return (int) (180 * radian / Math.PI);
	}
	
	/**
	 * 判断点是否在多边形内 
	 */
	public static boolean isPointInPolygon(GridPoint gp,List<GridPoint> plyA) {
		final CheckPolygon checkPolygon = new CheckPolygon();
		return checkPolygon.containsPoint(gp, plyA);
	}

	/**
	 * 判断两多边形边界是否相交
	 * @param plyA
	 * @param plyB
	 * @return 1包含，2相交
	 */
	public static int checkPolygonRelation(List<GridPoint> plyA, List<GridPoint> plyB) {// 面面
		int relation = 0;
		if (plyA.isEmpty() || plyB.isEmpty())
			return relation;

		final CheckPolygon checkPolygon = new CheckPolygon();
		boolean flag = true;
		for (GridPoint gp : plyA) {
			boolean one = checkPolygon.containsPoint(gp, plyB);
			if (one) {
				relation = 2;// 相交
			}
			flag = flag && one;
		}
		if (flag) {
			relation = 1;// 包含
			return relation;
		}

		for (GridPoint gp : plyB) {
			boolean one = checkPolygon.containsPoint(gp, plyA);
			if (one) {
				relation = 2;// 相交
			}
			flag = flag && one;
		}
		if (flag) {
			relation = 1;// 包含
		}

		return relation;
	}
	
	/**
	 * 判断多边形是否相交
	 * @param plyA
	 * @param plyB
	 */
	public static boolean isPolygonIntersect(List<GridPoint> plyA, List<GridPoint> plyB) {// 面面
		for (int i = 0; i < plyB.size() - 1; i++) {
			GridPoint gp1 = plyB.get(i);
			GridPoint gp2 = plyB.get(i+1);
			Line2D line = 	new Line2D.Double(gp1.lng, gp1.lat, gp2.lng, gp2.lat);
			boolean flag = checkPolygonRelation(plyA,line);

			if(flag) {
				return true;
			}
		}
		return false;
	}

	private static boolean checkPolygonRelation(List<GridPoint> plyA, Line2D line) {// 面面
		for (int i = 0; i < plyA.size()-1; i++) {
			GridPoint gp1 = plyA.get(i);
			GridPoint gp2 = plyA.get(i+1);

			Line2D edge = new Line2D.Double(gp1.lng, gp1.lat, gp2.lng, gp2.lat);
			if (line.intersectsLine(edge)) {
				return true;
			}
		}
		return false;
	}
	

	/**
	 * 判断一条直线和水平面的夹角
	 * 
	 * @param lat_a 纬度1
	 * @param lng_a 经度1
	 * @param lat_b 纬度2
	 * @param lng_b 经度2
	 * @return
	 */
   public static  double getAngle(double lng_a , double lat_a, double lng_b, double lat_b) {
       double y = Math.sin(lng_b-lng_a) * Math.cos(lat_b);
       double x = Math.cos(lat_a)*Math.sin(lat_b) - Math.sin(lat_a)*Math.cos(lat_b)*Math.cos(lng_b-lng_a);
       double brng = Math.atan2(y, x);

       brng = Math.toDegrees(brng);
       if(brng < 0)
           brng = brng +360;
       return brng;
   }

	/**
	 * 根据经纬度算出近似圆的经纬度
	 * 
	 * @param lon  经度
	 * @param lat  纬度
	 * @param dist 距离
	 * @param num 知道多少个点
	 * @return
	 */
	public static List<GridPoint> computerCirclePoint(double lon, double lat, double dist, int num) {
		List<GridPoint> list = new ArrayList<>();
		double onedir = 360 / num;
		double dir = 0;
		for (int i = 0; i < num; i++) {
			list.add(computerThatPoint(lon, lat, dir, dist));
			dir += onedir;
		}
		return list;
	}
   
   /**
    * 根据经纬度算出附近的正方形的四个角的经纬度
    * @param lon 经度
    * @param lat 纬度
    * @param dist 距离
    * @return
    */
   public static List<GridPoint> computerSquarePoint(double lon, double lat, double dist){
	   List<GridPoint> list = new ArrayList<>();
	   list.add(computerThatPoint(lon, lat, 0, dist));
	   list.add(computerThatPoint(lon, lat, 90, dist));
	   list.add(computerThatPoint(lon, lat, 180, dist));
	   list.add(computerThatPoint(lon, lat, 270, dist));
	   return list;
   }
   
   /**
	 * 由一条直线外扩一个长方形
	 * @param lng1 点1经度
	 * @param lat1 点1纬度
	 * @param lng2 点2经度
	 * @param lat2 点2纬度
	 * @param dist 距离
	 */
	public static List<GridPoint> computerRectanglePoint(double lng1,double lat1,double lng2,double lat2,double dist) {
		List<GridPoint> gpList = new ArrayList<>();
		double dir = 360-getAngle(lng1,lat1,lng2,lat2);
		GridPoint l1 = computerThatPoint(lng1,lat1, dir-90, dist);// 右
		GridPoint l2 = computerThatPoint(lng1,lat1, dir+90, dist);// 右
		gpList.add(l1);
		gpList.add(l2);

		GridPoint l3 = computerThatPoint(lng2,lat2, dir-90, dist);// 右
		GridPoint l4 = computerThatPoint(lng2,lat2, dir+90, dist);// 右
		gpList.add(l4);
		gpList.add(l3);
		return gpList;
	}
   
	/**
	 * 计算另一点经纬度
	 * 
	 * @param lon    经度
	 * @param lat    维度
	 * @param brng   方位角
	 * @param dist   距离（米）
	 */
	public static GridPoint computerThatPoint(double lon, double lat, double brng, double dist) {
		double alpha1 = rad(brng);
		double sinAlpha1 = Math.sin(alpha1);
		double cosAlpha1 = Math.cos(alpha1);

		double tanU1 = (1 - f) * Math.tan(rad(lat));
		double cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1));
		double sinU1 = tanU1 * cosU1;
		double sigma1 = Math.atan2(tanU1, cosAlpha1);
		double sinAlpha = cosU1 * sinAlpha1;
		double cosSqAlpha = 1 - sinAlpha * sinAlpha;
		double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
		double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
		double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));

		double cos2SigmaM = 0;
		double sinSigma = 0;
		double cosSigma = 0;
		double sigma = dist / (b * A), sigmaP = 2 * Math.PI;
		while (Math.abs(sigma - sigmaP) > 1e-12) {
			cos2SigmaM = Math.cos(2 * sigma1 + sigma);
			sinSigma = Math.sin(sigma);
			cosSigma = Math.cos(sigma);
			double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)
					- B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
			sigmaP = sigma;
			sigma = dist / (b * A) + deltaSigma;
		}

		double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
		double lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
				(1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
		double lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
		double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
		double L = lambda - (1 - C) * f * sinAlpha
				* (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));

//		double revAz = Math.atan2(sinAlpha, -tmp); // final bearing
		return new GridPoint(lon + deg(L), deg(lat2));
	}
	/**
	 * 度换成弧度
	 * 
	 * @param d 度
	 * @return 弧度
	 */
	private static double rad(double d) {
		return d * Math.PI / 180.0;
	}

	/**
	 * 弧度换成度
	 * 
	 * @param x 弧度
	 * @return 度
	 */
	private static double deg(double x) {
		return x * 180 / Math.PI;
	}
	
	
	
	public static void main(String[] args) {
		List<GridPoint> list = new ArrayList<>();
		list.add(new GridPoint(91.776494,29.206453));
		list.add(new GridPoint(91.756278,29.205034));
		list.add(new GridPoint(91.776278,29.205002));
		System.out.println(getPolylineDistance (list));
	}
}
